from svnClient import svnClient
from pimParser import pimParser
from pimIndexer import pimIndexer
pysvn = __import__('pysvn')
import os
import time
from sqliteDB import sqliteDB
from datetime import datetime
import commands
##@author Alessio Di Pietro
#@author Irene Scionti
#@brief methods for pim differing
class pimDiffer(object):
    '''
    Parse pim information at established time periods and understands when a pim file change.
    '''
    __diff_svnclients = [];
    def __init__(self, settings):
       
        '''
        Constructor
        '''
        self.__settings = settings
        #pim diff directory names and source files
        self.__diff_paths = [settings.contacts_diffname, settings.calendar_diffname, settings.memos_diffname];
        self.__diff_names = [settings.contactspath, settings.icspath, settings.memospath]
        self.__diff_sources = [settings.contactspath, settings.icspath, settings.memospath]
        
            
        
        '''
        svn clients list (one for each pim type)
        svnclients[0] -> contacts
        svnclients[1] -> calendar
        svnclients[2] -> memos
        
        '''
        self.__diffworkingcopy = settings.diffworkingcopy
        self.__svnWorkingCopy = settings.svnWorkingCopy
        self.__pimparser = pimParser(self.__settings)
        self.__pimparser.setSplitPath(settings.svnWorkingCopy)
        self.__pimindexer = pimIndexer(self.__settings)
        self.__added_entries = []
        self.__removed_entries = []
        
        for dir in self.__diff_paths:
            self.__diff_svnclients.append(svnClient(settings.svnWorkingCopy + dir, settings.svnServer, settings.svnRepoName, settings.svnUser, settings.svnPassword))
        
        self.__entry_svnclient = svnClient(settings.svnWorkingCopy, settings.svnServer, settings.svnRepoName, settings.svnUser, settings.svnPassword);
        self.__version_timer = settings.versiontimer;
        
        #database parameters
        self.__fts_db_name = settings.fts_db;
        self.__fts_db = sqliteDB(self.__svnWorkingCopy + '/index/' + self.__fts_db_name)
        self.__modified_db_table_names = ["modified_contacts", "modified_calendar", "modified_memos"]
        self.__removed_db_table_names = ["removed_contacts", "removed_calendar", "removed_memos"]
        
    
       
        if os.path.getsize(settings.svnWorkingCopy+'/index/'+settings.fts_db)==0:
            #database parameters
            fts_table_names=["contacts_index","calendar_index","memos_index"]
            
        
            #database creation: tables -->index,modified entries,removed_entries
            for table in fts_table_names:
                self.__fts_db.open().execute('CREATE VIRTUAL TABLE '+table+' USING fts3(uid VARCHAR,rev INTEGER,state,body,cutted,timestamp,removed_timestamp)')
                self.__fts_db.commit();
                self.__fts_db.close()
            for table in self.__modified_db_table_names:
                self.__fts_db.open().execute('CREATE TABLE '+table+' (uid VARCHAR PRIMARY KEY,revision INTEGER,timestamp VARCHAR)')
                self.__fts_db.commit();
                self.__fts_db.close()
            for table in self.__removed_db_table_names:
                self.__fts_db.open().execute('CREATE TABLE '+table+' (uid VARCHAR PRIMARY KEY,revision INTEGER,timestamp VARCHAR,removed_timestamp VARCHAR)')
                self.__fts_db.commit();
            self.__fts_db.close()

    ## @brief avoids hidden files or directory in ls command
    def nodot(self, item): 
        return item[0] != '.'
    ## @brief wrapper method for pim splitters
    # @param index entry type
    # @return to be copied entries
    def splitters(self, index):
        if index == 0:
            return self.__pimparser.contactSplit()
        elif index == 1:
            return self.__pimparser.icsSplit()
        elif index == 2:
            return self.__pimparser.memosSplit()
    ## @brief wrapper method for timestamp parsers
    # @param index entry type
    # @param entry_uid entry uid
    # @return the timestamp
    def tstamp_parsers(self, index, entry_uid):
        if index == 0:
            return self.__pimparser.contact_rev_parse(entry_uid)
        if index == 1:
            return self.__pimparser.calendar_dtstamp_parse(entry_uid)
        if index == 2:
            return self.__pimparser.memo_dtstamp_parse(entry_uid)
    ## @brief insert an entry into index db (modified or removed entries table)
    # @param entry_uid entry uid
    # @param revision revision tu be inserted
    # @param table_names names of tables (modified removed)
    # @oaram entry_type entry type
    # @param removed true if removed entry
    # @param changed true if modified entry
    # @param added true if added entry
    def insert_into_db(self, entry_uid, revision, table_names, entry_type, removed=False, changed=False, added=False):
        '''
        insert or update into table cointained in table_names list the entry uid (entry) and the revision (revision)
        '''
        c = self.__fts_db.open()
        if added:
            #if the entry is not present insert it with the revision number
            time_stamp = self.tstamp_parsers(entry_type, entry_uid)
            c.execute('INSERT INTO ' + table_names[entry_type] + '(uid,revision,timestamp) VALUES (?,?,?)', (entry_uid, revision, time_stamp))
            self.__fts_db.commit();
        if changed:
            #update revision value of the entry (rescued entry)
            c.execute('UPDATE ' + table_names[entry_type] + ' SET revision=? WHERE uid=?', (revision, entry_uid))
            self.__fts_db.commit();
        
        
        if removed:
            #retrieve creation timestamp from modified table
            c.execute('SELECT timestamp FROM ' + self.__modified_db_table_names[entry_type] + ' WHERE uid=?', (entry_uid,))
            timestamp = c.fetchone()[0]
            removed_timestamp = datetime.now().isoformat('T');
            #insert into removed table
            c.execute('INSERT INTO ' + table_names[entry_type] + '(uid,revision,timestamp,removed_timestamp) VALUES (?,?,?,?)', (entry_uid, revision, timestamp, removed_timestamp))
            #remove entry from MODIFIED_ENTRY table
            c.execute('DELETE FROM ' + self.__modified_db_table_names[entry_type] + ' WHERE uid=?', (entry_uid,))
            self.__fts_db.commit()
        
        self.__fts_db.close()     
          
    ##@brief execute the differing
    def differ(self):
        '''
        Execute versioning of differed entries (workingcopy vs evolution's files)
        '''
        os.system("svn cleanup "+self.__svnWorkingCopy)
        #for each entry type (contacts,calendar,memos)
        for i in range(3):
            
            #retrieving working copy files
            existing_files = filter(self.nodot, os.listdir(self.__svnWorkingCopy + self.__diff_paths[i]));
            #retrieve to be copied files (from splitting)           
            tobe_copied_files = self.splitters(i)
            try:
                #existing_files-tobe_copied_files= Deleted Events
                self.__removed_entries = [file for file in existing_files if not file in tobe_copied_files]
                #tobe_copied_files-existing_files= Added Events
                self.__added_entries = [file for file in tobe_copied_files if not file in existing_files]
        
                print 'Deleted ' + self.__diff_paths[i][1:] + '\n', self.__removed_entries
                print 'Added ' + self.__diff_paths[i][1:] + '\n', self.__added_entries
        
                #process added entries
                for added_entry in self.__added_entries:
                    #add entry to version control
                    self.__diff_svnclients[i].add("/" + added_entry, 'Added to version control at ' + time.asctime(time.localtime(time.time())))
                #process removed entries
                for removed_entry in self.__removed_entries:
                    #get entry revision
                    info_entry = self.__diff_svnclients[i].info("/" + removed_entry + "/" + removed_entry)
                    self.insert_into_db(removed_entry, info_entry.revision.number, self.__removed_db_table_names, i, removed=True)
                    #remove entry from version control
                    self.__diff_svnclients[i].revert("/" + removed_entry);
                    self.__diff_svnclients[i].update('head');
                    self.__diff_svnclients[i].remove("/" + removed_entry + "/" + removed_entry)
                    self.__diff_svnclients[i].remove("/" + removed_entry)

                
                #checks for changed entries
                changed_entries = self.__diff_svnclients[i].status("", pysvn.depth.infinity)
               
                #for each changed entry insert into index db the latest revision number
                for entry in changed_entries:
                    #insert entry into db
                    self.insert_into_db(os.path.basename(entry.path), entry.entry.revision.number + 1, self.__modified_db_table_names, i, changed=True)
               
                #commit svn operations
                self.__entry_svnclient.commit("Pim committed at " + time.asctime(time.localtime(time.time())))
             
                #for each added entry insert into index db the latest revision number
                for entry in self.__added_entries:
                    info_entry = self.__diff_svnclients[i].info("/" + entry + "/" + entry)
                    #insert entry into db
                    self.insert_into_db(entry, info_entry.revision.number, self.__modified_db_table_names, i, added=True)

            except Exception, e:
                print 'Error: ', e.args[0];
        #launch the indexer
        self.__pimindexer.indexing();
            
        
        
        

    
    
